#define LOG_TAG "IPLM_VehicleHelper"

#include "VehicleHelper.h"
#include <log/log.h>
#include <thread>
#include <chrono>

#include <cutils/properties.h>

namespace vendor {
namespace ecarx {
namespace xma {
namespace iplm {
namespace V1_0 {
namespace implementation {

using std::chrono::milliseconds;
using std::chrono::steady_clock;

VehicleHelper::VehicleHelper() {
    ALOGI("%s, constructor is called", __func__);

    mDMMFound = false;
    mDVRFound = false;
    mDIMFound = false;
    mASDMFound = false;
    mPASFound = false;
    mTCAM_DTC = false;
    mVGM_DTC = false;
    mASDM_DTC = false;
}

VehicleHelper::~VehicleHelper() {
    ALOGI("%s, deconstructor is called", __func__);
}

void VehicleHelper::init() {
    bool vehicle_ready = false;

    ALOGI("%s: Connecting to Vehicle HAL", __func__);
    if (mVehicle != nullptr) {
        ALOGD("%s: Vehicle Interface already initialized", __func__);
        return;
    }

    while (!vehicle_ready) {
        if (mVehicle == nullptr)
            mVehicle = IVehicle::tryGetService();

        if (mVehicle != nullptr) {
            if (mVehicle.get() != nullptr) {
                vehicle_ready = true;
                break;
            }
        }

        ALOGE("%s: try get Vehicle Service failed, will try again 500ms later", __func__);
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
    ALOGI("%s: Connected to Vehicle HAL successfully", __func__);
}

int32_t VehicleHelper::getCarConfig(int32_t config) {
    int32_t config_value = 0;

    VehiclePropValue v_prop = {
        .prop = config,
        .areaId = 1,
        .status = VehiclePropertyStatus::AVAILABLE,
        .timestamp = 0,
    };

    if (getValue(v_prop) && (v_prop.value.int32Values.size() > 0)) {
        config_value = v_prop.value.int32Values[0];
    }

    ALOGI("%s, config: %#x, value: %#x", __func__, config, config_value);
    return config_value;
}

bool VehicleHelper::getValue(VehiclePropValue & requestedProp) {
    Automotive_StatusCode status = Automotive_StatusCode::TRY_AGAIN;
    bool ret = false;

    if (mVehicle == nullptr || mVehicle.get() == nullptr) {
        ALOGE("%s, mVehicle is NULL", __func__);
        return false;
    }

        // Call the Vehicle HAL, which will block until the callback is complete
    mVehicle->get( requestedProp,
                  [&] (Automotive_StatusCode s, const VehiclePropValue& v) {
                      status = s;
                      requestedProp = v;
                      ret = true;
                  }
    );
    // ALOGD("%s: prop: %d status: %d ", __func__, requestedProp.prop, status);
    return ret;

}

bool VehicleHelper::setValue(VehiclePropValue setProp) {
    Automotive_StatusCode status = Automotive_StatusCode::TRY_AGAIN;

    if (mVehicle == nullptr || mVehicle.get() == nullptr) {
        ALOGE("%s, mVehicle is NULL", __func__);
        return false;
    }

    status = mVehicle->set(setProp);

    if (status == Automotive_StatusCode::OK) {
        // ALOGI("%s, set VehicleProp %#x success", __func__, setProp.prop);
        return true;
    } else {
        ALOGE("%s: failed with status %d", __func__, status);
        return false;
    }
}

bool VehicleHelper::isASDMAvailable() {
    if (mASDMFound) {
        return true;
    }

    if (getCarConfig(ASDM_CarConfig) == ASDM_) {
        mASDMFound = true;
        return true;
    }

    mASDMFound = false;
    return mASDMFound;
}

bool VehicleHelper::isDIMAvailable() {
    if (mDIMFound)
        return true;

    if (getCarConfig(DIM_CarConfig) == DIM_) {
        mDIMFound = true;
        return true;
    }

    mDIMFound = false;
    return mDIMFound;
}

bool VehicleHelper::isPASAvailable() {
    if (mPASFound)
        return true;

    if (getCarConfig(PAS_CarConfig) == PAS_) {
        mPASFound = true;
        return true;
    }

    mPASFound = false;
    return mPASFound;
}

bool VehicleHelper::isDVRAvailable() {
    if (mDVRFound)
        return true;

    if (getCarConfig(DVR_CarConfig) == DVR_) {
        return true;
    }
    return false;
}

bool VehicleHelper::isDMMAvailable() {
    if (mDMMFound)
        return true;

    if (getCarConfig(DMM_CarConfig) == DMM_) {
        mDMMFound = true;
        return true;
    }

    mDMMFound = false;
    return mDMMFound;
}

int32_t VehicleHelper::RG_1_checkin() {
    return property_get_int32(kIplmNodesRG1, DEF_NUMBER_NODES_RG1);
}

int32_t VehicleHelper::RG_2_checkin() {
    int32_t count = property_get_int32(kIplmNodesRG2, DEF_NUMBER_NODES_RG2);

    if (isDMMAvailable()) {
        return count;
    }
    return (count - 1);
}

int32_t VehicleHelper::RG_3_checkin() {
    return property_get_int32(kIplmNodesRG3, DEF_NUMBER_NODES_RG3);
}

int32_t VehicleHelper::RG_4_checkin() {
    int32_t count = property_get_int32(kIplmNodesRG4, DEF_NUMBER_NODES_RG4);

    if (!isDVRAvailable()) {
        count --;
    }
    if (!isPASAvailable()) {
        count --;
    }
    return count;
}

int32_t VehicleHelper::RG_5_checkin() {
    int32_t count = property_get_int32(kIplmNodesRG5, DEF_NUMBER_NODES_RG5);

    if (!isASDMAvailable()) {
        count --;
    }
    return count;
}

int32_t VehicleHelper::RG_6_checkin() {
    int32_t count = property_get_int32(kIplmNodesRG6, DEF_NUMBER_NODES_RG6);

    if (!isDMMAvailable()) {
        count --;
    }

    if (!isDIMAvailable()) {
        count --;
    }
    return count;
}

int32_t VehicleHelper::RG_7_checkin() {
    return property_get_int32(kIplmNodesRG7, DEF_NUMBER_NODES_RG7);
}

bool VehicleHelper::isUDPLinkLostDTCSet(const ECU_Node node) {
    switch (node) {
        case NODE_ASDM:
            if (mASDM_DTC)
                return true;
            break;
        case NODE_TCAM:
            if (mTCAM_DTC)
                return true;
            break;
        case NODE_VGM:
            if (mVGM_DTC)
                return true;
            break;
        default:
            break;
    }

    return false;

}

void VehicleHelper::setDID() {

    uint8_t vlan2_pcp = static_cast<uint8_t>(property_get_int32(kVLAN2PCP, DEF_VLAN2_PRIORITY));
    uint8_t vlan3_pcp = static_cast<uint8_t>(property_get_int32(kVLAN3PCP, DEF_VLAN3_PRIORITY));
    uint8_t vlan4_pcp = static_cast<uint8_t>(property_get_int32(kVLAN4PCP, DEF_VLAN4_PRIORITY));
    uint8_t vlan5_pcp = static_cast<uint8_t>(property_get_int32(kVLAN5PCP, DEF_VLAN5_PRIORITY));
    uint8_t vlan6_pcp = static_cast<uint8_t>(property_get_int32(kVLAN6PCP, DEF_VLAN6_PRIORITY));
    uint8_t vlan10_pcp = static_cast<uint8_t>(property_get_int32(kVLAN10PCP, DEF_VLAN10_PRIORITY));
    uint8_t vlan11_pcp = static_cast<uint8_t>(property_get_int32(kVLAN11PCP, DEF_VLAN11_PRIORITY));
    uint8_t vlan12_pcp = static_cast<uint8_t>(property_get_int32(kVLAN12PCP, DEF_VLAN12_PRIORITY));

    ALOGD("%s, [vlan2_pcp: %d], [vlan3_pcp: %d], [vlan4_pcp: %d], [vlan5_pcp: %d], [vlan6_pcp: %d], \
            [vlan10_pcp: %d], [vlan11_pcp: %d], [vlan12_pcp: %d]", __func__, vlan2_pcp, vlan3_pcp,
            vlan4_pcp, vlan5_pcp, vlan6_pcp, vlan10_pcp, vlan11_pcp, vlan12_pcp);

    /*VehiclePropValue val = {//信号依赖
        .prop = static_cast<int32_t>(VendorVehicleProperty::CB_Dcm_D02E_VLANPriority),
        .areaId = 1,
        .status = VehiclePropertyStatus::AVAILABLE,
        .timestamp = 0,
        .value.bytes = std::vector<uint8_t>(4, 0),
    };

    val.value.bytes[0] = vlan3_pcp | (vlan2_pcp<<4);
    val.value.bytes[1] = vlan5_pcp | (vlan4_pcp<<4);
    val.value.bytes[2] = vlan10_pcp | (vlan6_pcp<<4);
    val.value.bytes[3] = vlan12_pcp | (vlan11_pcp<<4);
    ALOGD("%s: set iplm_vehicle_set_vlan_priority 0x%04x 0x%04x 0x%04x 0x%04x", __func__,
            val.value.bytes[0], val.value.bytes[1], val.value.bytes[2], val.value.bytes[3]);

    setValue(val);*/
}

void VehicleHelper::setUDPLinkLostDTC(const ECU_Node node, IPLM_DTC_VALUE value) {
    int32_t temp_prop;
    std::string node_name;
    std::string dump_dtc;

    switch (node) {
        case NODE_ASDM: {//信号依赖
            node_name = std::string("NODE_ASDM");
            //temp_prop = static_cast<int32_t>(VendorVehicleProperty::CB_Dem_LostUDPCommunicationWithASDM);
            mASDM_DTC = (value == IPLM_DTC_VALUE::REPORT_DTC);
            break;
        }
        case NODE_VGM: {
            node_name = std::string("NODE_VGM");
            //temp_prop = static_cast<int32_t>(VendorVehicleProperty::CB_AP_D0B000_VGM_Not_Connected_On_Ethernet);
            mVGM_DTC = (value == IPLM_DTC_VALUE::REPORT_DTC);
            break;
        }
        case NODE_TCAM: {
            node_name = std::string("NODE_TCAM");
            //temp_prop = static_cast<int32_t>(VendorVehicleProperty::CB_Dem_LostUDPCommunicationWithTCAM);
            mTCAM_DTC = (value == IPLM_DTC_VALUE::REPORT_DTC);
            break;
        }
        default: {
            //ALOGI("%s, igore this unknown DTC %#x", __func__, node);
            return;
        }
    }

    ALOGI("%s, set UDPLinkLost DTC [%s, value: %#x]", __func__, node_name.c_str(), value);

    /*VehiclePropValue val = {//信号依赖
        .prop = temp_prop,
        .areaId = 1,
        .status = VehiclePropertyStatus::AVAILABLE,
        .timestamp = 0,
        .value.int32Values = std::vector<int32_t> { 0 }
    };

    val.value.int32Values[0] = value;
    setValue(val);*/
}

void VehicleHelper::setPowerState(IPLM_Power_State status) {//信号依赖
    VehiclePropValue val({
        .prop = static_cast<int32_t>(DirectTransmitProperty::CB_Power_IPLMInfo),
        .areaId = 1,
        .status = VehiclePropertyStatus::AVAILABLE,
        .timestamp = 0,
        .value.int32Values = std::vector<int32_t>{ 0 }
    });

    val.value.int32Values[0] = status;
    ALOGD("%s: set iplm_vehicle_set_CB_Power_IPLMInfo: %d", __func__, status);

    setValue(val);
}

void VehicleHelper::wakeupVFC() {//信号依赖
    ALOGI("%s is triggered", __func__);

    VehiclePropValue val = {
        .prop = static_cast<int32_t>(DirectTransmitProperty::CB_VFC_IPWakeup),
        .areaId = 1,
        .status = VehiclePropertyStatus::AVAILABLE,
        .timestamp = 0,
        .value.int32Values = std::vector<int32_t>{ 0 }
    };

    val.value.int32Values[0] = 1;
    setValue(val);
}

} // namespace implementation
} // namespace V1_0
} // namespace iplm
} // namespace xmas
} // namespace ecarx
} // namespace vendor